use anyhow::Result;
use serde_derive::{Deserialize, Serialize};

use crate::errors::RuntimeError;

#[derive(Debug, Deserialize, Serialize, Clone,Default)]
pub struct App {
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub http: Option<HttpConf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub database: Option<DbCnf>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    #[serde(default)]
    pub log_level: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub admin_op_secret : Option<String>,
}


impl App {
    pub fn from_string(data: &str) -> Result<Self, RuntimeError> {
        match toml::from_str::<App>(data) {
            Ok(app) => Ok(app),
            Err(e) => Err(RuntimeError::InternalError(e.to_string())),
        }
    }
    pub fn from_file(filename: &str) -> Result<Self, RuntimeError> {
        match std::fs::read_to_string(filename) {
            Ok(data) => App::from_string(&data),
            Err(e) => Err(RuntimeError::InternalError(e.to_string())),
        }
    }

    pub fn copy_to_echo(&self) -> App {
        App {
            http: None,
            database: None,
            name: self.name.clone(),
            log_level: self.log_level.clone(),
            admin_op_secret: self.admin_op_secret.clone(),
        }
    }

    pub fn database(&self) -> Result<&DbCnf, RuntimeError> {
        match &self.database {
            Some(db) => Ok(&db),
            None => Err(RuntimeError::InternalError("no database configuration".to_string())),
        }
    }
    pub fn http(&self) -> Result<&HttpConf, RuntimeError> {
        match &self.http {
            Some(http) => Ok(&http),
            None => Err(RuntimeError::InternalError("no http configuration".to_string())),
        }
    }
}

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct HttpConf {
    pub ip: String,
    pub port: u32,
}

impl HttpConf {
    pub fn ip_str(&self) -> String {
        format!("{}:{}", self.ip, self.port)
    }
}

#[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct DbCnf {
    pub host: String,
    pub port: u32,
    pub user: String,
    pub password: String,
    pub database: String,
}

impl DbCnf {
    #[allow(dead_code)]
    pub fn to_dsn(&self) -> String {
        format!(
            "mysql://{}:{}@{}:{}/{}",
            self.user, self.password, self.host, self.port, self.database,
        )
    }
}
